#!/usr/bin/python
import os, sys, pdb, time, zestlib as zest
from zestlib import which
################################################################################
################################################################################
def UpdateMessage( checkmsg ):
    checkmsg = '%-30s' % checkmsg
    print 'depcheck.py is checking:', checkmsg,

def UpdateError( errmsg ):
    print '\n... potential issue:', errmsg,

def UpdateFinal( status ):
    if status: print ' -- ok'
    else:      print '\n...'
    return status

################################################################################
################################################################################
def CheckMSRModule():
    UpdateMessage( 'msr module' )
    if os.path.exists( '/dev/cpu/0/msr' ):
        return UpdateFinal( True )
    cmd = 'lsmod'
    lines = zest.SystemWrapperPipelined( cmd )
    for line in lines:
        toks = line.split()
        if toks[0] == 'msr':
            return UpdateFinal( True )
    UpdateError( 'cannot find msr module'  )
    UpdateError( 'try "sudo modprobe msr"' )
    return UpdateFinal( False )

def WhichWrapper( cmd ):
    UpdateMessage( cmd )
    cmdpath = which( cmd ) 
    if cmdpath is None:
        UpdateError( cmd + ' is not installed' )
        return UpdateFinal( False )
    return UpdateFinal( True )

################################################################################
################################################################################
def ExtractKVerApt( lines, checkitem ):
    if len( lines ) == 0: return None
    kvers = []
    for line in lines:
        line = line.replace( checkitem + '-', '' )
        toks = line.split()
        kver = toks[0]
        kvers.append( kver )
    return kvers

class NormalizedVersion:
    def __eq__( self, other ):
        if( self.kernel != other.kernel ): 
            return False
        if( self.major != other.major ):
            return False
        if( self.minor != other.minor ):
            return False
        if( self.fix != other.fix ):
            return False
        return True
    
    def __ne__( self, other ):
        if( __eq__( self, other ) ):
            return False
        return True
    
    def __lt__( self, other ):
        if( self.kernel < other.kernel ): 
            return True
        if( self.major < other.major ):
            return True
        if( self.minor < other.minor ):
            return True
        if( self.fix < other.fix ):
            return True
        return False
    
    def __le__( self, other ):
        if( __eq__( self, other ) ):
            return True
        if( __lt__( self, other ) ):
            return True
        return False
    
    def __gt__( self, other ):
        if( self.kernel > other.kernel ): 
            return True
        if( self.major > other.major ):
            return True
        if( self.minor > other.minor ):
            return True
        if( self.fix > other.fix ):
            return True
        return False
    
    def __ge__( self, other ):
        if( __eq__( self, other ) ):
            return True
        if( __gt__( self, other ) ):
            return True
        return False
    
    def __init__( self, version ):
        if( version.find('-') != -1 ):
            self.update = version[ version.find('-')+1 : ]
            if( self.update.find('.') ):
                self.update = self.update[ : self.update.find('.') ]
            self.update = int( self.update )
            version = version[ : version.find('-') ]
        else:
            self.update = int( 0 )
        
        if( version.find(':') != -1 ):
            self.kernel = int( version[ 0 : version.find(':') ] )
            version = version[ version.find(':')+1 : ]
        else:    
            self.kernel = int( version[ 0 : version.find('.') ] )
            version = version[ version.find('.')+1 : ]
        self.major = int( version[ 0 : version.find('.') ] )        
        version = version[ version.find('.')+1 : ]
        if( version.find('.') != -1 ):        
            self.minor = int( version[ 0 : version.find('.') ] )
            version = version[ version.find('.')+1 : ]
            self.fix = int( version )
        else:
            self.minor = int( version )
            self.fix = int( 0 )
    

def MaxVersion( kvers ):
    if len( kvers ) == 0: return None
    maxver = kvers[0]
    kvers.remove( maxver )
    for kver in kvers:
        if( NormalizedVersion( kver ) > NormalizedVersion( maxver ) ):
            maxver = kver
    return maxver

def ExtractKVerYum( lines ):
    if len( lines ) == 0: return None
    kvers = []
    for line in lines:
        toks = line.split()
        assert len( toks ) >= 2
        kvers.append( toks[1] )
    return MaxVersion( kvers )

def ParseZypLines( lines, matchtok ):
    if len( lines ) == 0: return False
    for line in lines:
        toks = line.split()
        if len( toks ) < 3: continue
        assert toks.pop( 0 ) == 'i'
        assert toks.pop( 0 ) == '|'
        if toks.pop( 0 ) == matchtok:
            return True
    return False

def DoAptCheck():
    ok = True
    checklist = [ 'linux-headers', 'dmidecode', 'msr-tools' ]
    checkdict = {}
    for checkitem in checklist:
        cmd = 'dpkg --get-selections | grep ' + checkitem
        lines = zest.SystemWrapperPipelined( cmd )
        kvers = ExtractKVerApt( lines, checkitem )
        checkdict[ checkitem ] = kvers
    kver = zest.SystemWrapperPipelined( 'uname -r' )[0]
    for k, v in checkdict.iteritems():
        itemok = True
        UpdateMessage( k )
        if v is None:
            UpdateError( k + ' not installed' )
            itemok = False
        elif k.count( 'linux' ):
            if kver not in v:
                UpdateError( 'version %s installed for %-15s but kernel is %s' % ( v, k, kver ) )
                itemok = False
        UpdateFinal( itemok )
        ok = ok and itemok
    ok = CheckMSRModule()      and ok
    ok = WhichWrapper( 'g++' ) and ok
    return ok

def DoYumCheck():
    ok = True
    checklist = [ 'kernel-headers', 'kernel-devel', 'dmidecode' ]
    checkdict = {}
    for checkitem in checklist:
        cmd = 'yum list installed | grep ' + checkitem
        lines = zest.SystemWrapperPipelined( cmd )
        kver  = ExtractKVerYum( lines )
        checkdict[ checkitem ] = kver
    kver = zest.SystemWrapperPipelined( 'uname -r' )[0]
    for k, v in checkdict.iteritems():
        itemok = True
        UpdateMessage( k )
        if v is None:
            UpdateError( k + ' not installed' )
            itemok = False
        elif k.count( 'kernel' ):
            kversuffix = kver[kver.rfind('.')+1:]   # Handle kver.x86_64
            if v != kver:
                vext = v
                vext += '.' + kversuffix
                if vext != kver:
                    UpdateError( 'version %s installed for %-15s but kernel is %s' % ( v, k, kver ) )
                    itemok = False
        UpdateFinal( itemok )
        ok = ok and itemok
    ok = WhichWrapper( 'g++' ) and ok
    return ok

def DoZypCheck():
    ok = True
    checklist = [ 'gcc', 'gcc-c++', 'glibc-devel', 'kernel-default-devel' ]
    for checkitem in checklist:
        itemok = True
        UpdateMessage( checkitem )
        cmd = 'zypper search -i | grep ' + checkitem
        lines = zest.SystemWrapperPipelined( cmd )
        itemok = ParseZypLines( lines, checkitem )
        if not itemok:
            UpdateError( 'missing package: %s, try "sudo zypper install %s"' % ( checkitem, checkitem ) )
        UpdateFinal( itemok )
        ok = ok and itemok
    ok = WhichWrapper( 'dmidecode' ) and ok
    ok = WhichWrapper( 'g++' )       and ok
    CheckMSRModule()
    return ok

################################################################################
################################################################################

yum  = which( 'yum' )
apt  = which( 'apt-get' )
zyp  = which( 'zypper' )
rpm  = which( 'rpm' )
dpkg = which( 'dpkg' )

if   apt and dpkg is not None: ok = DoAptCheck()
elif yum and rpm  is not None: ok = DoYumCheck()
elif zyp          is not None: ok = DoZypCheck()
else:
    print 'cannot find either apt-get/dpkg or yum/rpm, not sure of package mgr'
    sys.exit( -1 )

if not ok:
    sys.exit( -1 )
